We have, of course, encountered variables in the previous chapter, where we used some for string substitution. For a functional programming language, mastery of how variables work is essential. Julia is extremely flexible when it comes to variables, but wielding this flexibility will require finesse.
In [2]:
m = 2.32
Out[2]:
In [3]:
m
Out[3]:
Accessing an undefined variable yields an exception, of course:
In [4]:
n
Julia does not require you to explicitly declare variables before assignment (indeed, there is no useful way to do so).
In general, you can use just about anything you can type as a variable name. This includes Unicode characters from a quite astounding range.
In the following, we will be proving, using the comparison operator (>), that winter is not warmer than summer:
In [5]:
❄ = -12
Out[5]:
In [6]:
✹ = 27
Out[6]:
In [7]:
❄ > ✹
Out[7]:
The above is, believe it or not, perfectly valid Julia. Whether it is perfectly sensible Julia, too, is a different question.
In general, using Unicode variable names for anything but commonly accepted scientific symbols, such as ћ, is not the best idea, lest you might leave readers of your code trying to figure out which of the thirty or so similar looking Unicode symbols you meant.
The stylistic convention of Julia is to use lowercase variable names, with words separated by _ (underscore), and only if necessary for the sake of legibility. Variables may not start with numbers and exclamation marks.
In 1894, an amateur mathematician from Indiana, Edward J. Goodwin, proposed a way to square the circle (a feat proven to be impossible by the Lindeman-Weierstrass theorem proven a decade or so earlier). He lobbied long enough for his 'new mathematical truth' to be introduced as a Bill in the Indiana House of Representatives, in what became known as the Indiana Pi Bill of 1897. This inferred a value of approximately 3.2 for π. Fortunately, the intervention of Purdue professor C.A. Waldo helped to defeat the already much-ridiculed bill in the Senate, and the matter was laid to rest.
Julia, on the other hand, allows you to redefine the value of π. The following is entirely correct Julia:
In [8]:
π
Out[8]:
In [9]:
π = 3.2
Out[9]:
In [10]:
π
Out[10]:
It is, on the other hand, really bad practice to redefine set constants. Therefore, should you encounter the opportunity to redefine π, learn from the sad case of Mr. Goodwin and try to resist the temptation.
It is important to understand what the effect of assigning a variable to a variable is. It is perfectly valid Julia to have multiple variables point at each other.
However, doing so creates a 'shallow copy' – that is, a reference to the same memory address of where the original copy is located.
If you modify one variable pointing at it, the value of the other changes, too. In the following example, we will be creating an array m, then set n be equal to m. We then carry out an operation that changes the value of m, marked by the exclamation mark (don't worry if that part is new to you, it will be explained in Chapter 6 – for now, all you need to know is that pop! takes the last element of an indexable collection, returns it and removes it from the collection).
When we then call n, we see that its value, too, has been affected by the operation – that is because it did not so much have a value but acted merely as a reference to 'whatever is in m'.
In [11]:
m = [1,2,3,4] # Creating array
Out[11]:
In [12]:
n = m # Setting n to point to m
Out[12]:
In [13]:
pop!(m) # Altering m
m
Out[13]:
In [14]:
n # n is also changed as a result.
Out[14]:
We will be considering a way to get around this by 'deep copying' a value in a later chapter.
In [16]:
"Hi Github!"
Out[16]:
In [17]:
'Hi Github!'
In [18]:
arr1 = [1, 2, "sausage", π]
Out[18]:
In Julia, a range is simply a shorthand for a sequence of numbers that are all spaced equally.
A range can be created using the range() function as range(start, end), but it is usually denoted in a shorthand literal, start:end. Ranges are interpreted as arrays, and you can create range arrays, arrays that are formed from a range, by simply enclosing the range notation in brackets:
In [22]:
[0:10]
Out[22]:
As you can see, a range in Julia includes both its start and end element.
A range doesn't have to be created between integers – [0.5:3.5] returns:
In [23]:
[0.5:3.5]
Out[23]:
An array may have an optional middle step attribute, which takes the form start:step:end.
To obtain an array of the numbers from 0 to 30 in steps of 10, you would enter the range array literal [0:10:30]:
In [28]:
[0:10:30]
Out[28]:
In [51]:
non_md_array = [[1,2,3,4],[3,4,5,6],[5,6,7,8],[7,8,9,10]]
Out[51]:
In [65]:
still_non_md_array = [[0:10:30]; [0:10:30]]
Out[65]:
Rather, to create a multidimensional array, separate values by empty space and rows by a ; (semicolon).
In [58]:
fancy_md_array = [[1 2 3 4]; [3 4 5 6]; [5 6 7 8]; [7 8 9 10]] # with brackets
Out[58]:
In [63]:
bracketless_md_array = [1 1 2 3 5; 8 13 21 34 55; 89 144 233 377 610] # without brackets
Out[63]:
Alternatively, you can enter columns, using square brackets (remember not to let your ingrained reflexes from Python take over and put a comma between the arrays!):
In [72]:
spaced_md_array = [[1,2,3] [4,5,6]] # without semicolons
Out[72]:
In [73]:
step_based_md_array = [[0:10:30] [0:10:30]]
Out[73]:
When entering a multidimensional array, each row has to have the same length. Failing to do so raises an error:
In [74]:
md_sparse_array = [1 1 2; 8 13 21 34 55]
Tuples are similar to one-dimensional arrays, in that they consist of a number of values. They are, however, unlike array literals in that they are immutable – once assigned, you cannot change the values in a tuple. Tuples are delimited by () (round brackets) and values are separated by commas.
In [75]:
fibo_tuple = (1, 1, 2, 3, 5)
Out[75]:
To demonstrate the difference between arrays and tuples, consider the following:
In [78]:
fibo_array = [1, 1, 2, 3, 5]
Out[78]:
In [81]:
fibo_arr[2] = 0 # Change the value at index 2 to 0
Out[81]:
In [82]:
fibo_arr # Show the new array
Out[82]:
In [83]:
fibo_tuple[2] = 0 # Set tuple value at index 2 to 0
In this listing, we have created an array with the first five non-zero elements of the Fibonacci sequence. We then have used an accessor (fibo_arr[2] is an accessor that retrieves the second element of the array fibo_arr) to change the second value in the array to zero.
Calling the array shows that this was successful. On the other hand, trying to do the same with the tuple fibo_tuple of the same values that we declared earlier yields an error. This is because tuples are immutable; the values they are created with are their final values.
In [91]:
statisticians = Dict("Gosset" => "1876-1937", "Pearson" => "1857-1936", "Galton" => "1822-1911") # Current syntax
Out[91]:
In [92]:
bracketed_statisticians = ["Gosset" => "1876-1937", "Pearson" => "1857-1936", "Galton" => "1822-1911"] # Old syntax
Out[92]:
Sets are similar to other collections in that they contain various values. However, unlike arrays and tuples, they are unordered and unique-constrained: no element may occur multiple times within the set.
Sets do not have a specific notation, but rather are created using a syntax we will use a for creating all kinds of objects: by using the type (Set) and entering the values to constitute the set in whatever form they come in (typically arrays with square braces):
In [108]:
numbers = Set([1,2,3,4,1])
Out[108]:
In [111]:
numbers = Set(["Hi","Hi"])
Out[111]:
In [112]:
stooges = Set(["Moe", "Curly", "Larry"])
Out[112]:
Sets do accept duplicates at time of construction, but the resulting set will still only contain one of each unique element:
In [113]:
beatles = Set(["Lennon", "McCartney", "Harrison", "Starr", "Lennon"]) # The first Lennon is getting kicked out of the Beatles
Out[113]:
Sets are unordered, meaning that two sets containing the same elements are equal:
In [114]:
Set(["Marsellus", "Jules", "Vincent"]) == Set(["Jules", "Vincent", "Marsellus"])
Out[114]:
An empty set is created by
In [118]:
Set([])
Out[118]:
In [117]:
Set()
Out[117]: